<?php
/**
 * 发送远程请求
 * User: Administrator
 * Date: 2018/3/29
 * Time: 10:05
 */

namespace app\models;

use linslin\yii2\curl\Curl;
use yii\base\Model;

class RequestModel extends Model
{

    // WxPay实例本身
    protected static $instance;

    // 详细错误
    private $err_detail;

    /**
     * 请求类型
     * @var array
     */
    private static $requestType = [
        'GET' => 'GET',
        'POST' => 'POST',
        'XML' => 'XML',
        'JSON' => 'JSON'
    ];

    /**
     * 请求域名
     * @var
     */
    protected $url;

    /**
     * 传输参数
     * @var array
     */
    protected $params = [];

    /**
     * 请求类型|默认 post
     * @var string
     */
    protected $method = 'POST';

    /**
     * 存储结果
     * @var array
     */
    protected $result = [];

    /**
     * 超时间
     * @var int
     */
    protected $second = 30;

    /**
     * header
     * @var
     */
    protected $header;

    /**
     * [header]预设
     * @var array
     */
    public static $headerSetting = [
        'Content-Type'=> 'application/x-www-form-urlencoded; charset=utf-8',
        // 'Content-Length' => 9999999
    ];

    /**
     * 验证规则
     * @return array
     */
    public function rules()
    {

        // 请求类型列表
        $methodList = array_values(self::getMethodList());

        return [
            [['url', 'method'], 'required'],
            ['method', 'in', 'range' => $methodList],
            ['url', 'url'],
            [['params', 'header'], 'safe']
        ];
    }

    public function attributeLabels()
    {
        return [
            'requestType' => '请求类型预设',
            'method' => '请求类型',
            'url' => '请求链接'
        ];
    }

    /**
     * 加载整体[Model]
     * @param array $data 初始化的数据
     * @return RequestModel
     */
    public static function loadModel($data = [])
    {

        return self::getInstance()->initInstance($data);
    }

    /**
     * 获取实例
     * @return RequestModel
     */
    protected static function getInstance()
    {

        // 实例不存在初始化
        if (empty(self::$instance))

            self::$instance = new self();

        // 存在直接返回
        return self::$instance;
    }

    /**
     * 初始化一些默认信息
     * @param array $_data
     * @return RequestModel
     */
    protected function initInstance($_data = [])
    {

        // 获取实例
        $instance = self::getInstance();

        $data = [];
        // ******** 做一些参数的赋值 ********
        // 1、链接
        if (!empty($_data['url'])) {

            $data['url'] = $_data['url'];
        }

        // 2、请求类型
        $methodList = array_values(self::getMethodList());
        if (!empty($_data['method']) && in_array($_data['method'], $methodList)) {

            $data['method'] = $_data['method'];
        } else {

            $data['method'] = self::getMethodPost();
        }

        // 3、请求参数
        if (!empty($_data['params'])) {

            $data['params'] = $_data['params'];
        }

        // 3、header
        if (!empty($_data['header'])) {

            $data['header'] = $_data['header'];
        }

        // 参数赋值
        if (!empty($data))

            $instance->setAttributes($data);

        // 返回
        return $instance;
    }

    /**
     * 发送请求
     * @return bool
     */
    public function doRequest()
    {

        try {

            $method = $this->method;

            // 请求参数有
            $options = $this->params;
            // json_encode($this->options, JSON_UNESCAPED_UNICODE);

            // 发送请求
            // 实例化curl
            $curl = new Curl();

            $response_transit = '';

            if (!empty($this->header)) {

                $curl->setHeaders($this->header);
            }

            // 判断是 get
            if (strtoupper($method) == self::getMethodGet()) {

                $response_transit = $curl
                    ->setGetParams($options)
                    ->get($this->url);
            } else if (strtoupper($method) == self::getMethodPost()) { // 判断是post

                $response_transit = $curl
                    ->setPostParams($options)
                    ->post($this->url);
            } else if (strtoupper($method) == self::getMethodXML()) { // 判断是XML

                $params = "<xml>";
                $params .= $this->arrayToXml($options);
                $params .= "</xml>";
                $response_transit = $curl
                    ->setRawPostData($params)
                    ->post($this->url);
            } else if (strtoupper($method) == self::getMethodJSON()) { // 判断是post

                $options = json_encode($options, JSON_UNESCAPED_UNICODE);
                $response_transit = $curl
                    ->setRequestBody($options)
                    ->post($this->url);
            }

            // 返回内容数组化
            // $rdata = json_decode($response_transit, true);
            $rdata = $response_transit;

            if (!$rdata) {

                // 返回 false 就是有错误
                switch ($curl->responseCode) {

                    case 'timeout':
                        $this->addError('timeout', '请求链接未响应');
                        break;

                    case 500:
                        //500 logic here
                        $this->addError('500', '服务器错误网页暂时无法访问');
                        break;

                    case 404:
                        //404 Error logic here
                        $this->addError('404', '页面未找到');
                        break;

                    case 302:
                        //404 Error logic here
                        $this->addError('302', '页面重定向');
                        break;
                }

                // 还有一种错误情况
                if (!empty($curl->errorCode) || !empty($curl->errorText)) {
                    $this->addError($curl->errorCode, $curl->errorText);
                }

                return false;
            }

            $this->result = $rdata;

            // 未出错返回内容
            return true;
        } catch (\Exception $exception) {

            // 记录下错误日志
            \Yii::error([
                "************************* 请求发送类错误 *************************",
                "错误详情：请求发送失败",
                $exception->getMessage(),
                "错误详情: ",
                json_encode($exception, JSON_UNESCAPED_UNICODE)
            ], 'normal');

            $this->addError('500', '请求发送失败：' . $exception->getMessage());
            return false;
        }
    }

    /**
     * 获取发送请求结果
     * @return array|string
     */
    public function getResult()
    {

        // 如果走的是XML返回转换下
        if (strtolower($this->method) == 'xml')

            $result = $this->FromXml($this->result);
        else

            $result = $this->result;

        return $result;
    }

    /**
     * 数组xml字符
     * @param array $params
     * @return string
     */
    protected function arrayToXml($params = [])
    {

        if (empty($params))

            return '';

        $xml = "";
        foreach ($params as $key => $val) {

            if (is_array($val)) {
                $xml .= $this->arrayToXml($val);
                continue;
            }
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }

        return $xml;
    }

    /**
     * xml转为array
     * @param $xml
     * @return mixed|string
     */
    public function FromXml($xml)
    {

        if (!$xml) {
            return '';
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $values;
    }

    /**
     * 获取错误中文
     * @param $code
     * @return mixed|string
     */
    private function getErrMsgForChinese($code)
    {
        $data = [

        ];

        if (empty($data[$code])) {
            return '未知错误';
        }

        return $data[$code];
    }

    /**
     * 请求类型[GET]值
     * @return mixed
     */
    public static function getMethodGet() {

        return self::$requestType['GET'];
    }
    /**
     * 请求类型[POST]值
     * @return mixed
     */
    public static function getMethodPost() {

        return self::$requestType['POST'];
    }
    /**
     * 请求类型[XML]值
     * @return mixed
     */
    public static function getMethodXML() {

        return self::$requestType['XML'];
    }
    /**
     * 请求类型[JSON]值
     */
    public static function getMethodJSON() {

        return self::$requestType['JSON'];
    }
    /**
     * 请求类型[全部]值
     * @return mixed
     */
    public static function getMethodList() {

        return self::$requestType;
    }
}
